/**
 * Copyright (C) 2008 by The Regents of the University of California
 * Redistribution of this file is permitted under the terms of the GNU
 * Public License (GPL).
 *
 * @author Junghoo "John" Cho <cho AT cs.ucla.edu>
 * @date 3/24/2008
 */

#include <cstdio>
#include <iostream>
#include <fstream>
#include "Bruinbase.h"
#include "SqlEngine.h"
#include "BTreeIndex.h"

using namespace std;

// external functions and variables for load file and sql command parsing 
extern FILE* sqlin;
int sqlparse(void);


RC SqlEngine::run(FILE* commandline)
{
  fprintf(stdout, "Bruinbase> ");
  // set the command line input and start parsing user input
  sqlin = commandline;
  
  sqlparse();  // sqlparse() is defined in SqlParser.tab.c generated from
               // SqlParser.y by bison (bison is GNU equivalent of yacc)

  return 0;
}

RC SqlEngine::select(int attr, const string& table, const vector<SelCond>& cond)
{
  RecordFile rf;   // RecordFile containing the table
  RecordId   rid;  // record cursor for table scanning
	
  RC     rc;
  int    key;     
  string value;
  int    count;
  int    diff;

  // open the table file
  if ((rc = rf.open(table + ".tbl", 'r')) < 0) {
    fprintf(stderr, "Error: table %s does not exist\n", table.c_str());
    return rc;
  }
  // scan the table file from the beginning
  rid.pid = rid.sid = 0;
  count = 0;

  BTreeIndex index;
  const string name = table + ".idx";
  index.open(name, 'r');
  IndexCursor cursor;
  RecordId rid2;
  RecordId rid3;
  int key2;
  int key3;
  int useNonIndex = 0;
 // RecordFile RecFile;
  string value2;
  string value3;
	ifstream filestr;
	int exist = 0;
	string indexName = table + ".idx";
	
	filestr.open (indexName.c_str(),ifstream::out);
	if(filestr){
		exist = 1;
		filestr.close();
	}
  
  if(exist == 1){//also need to check if value isn't a character
	if(cond.empty()){
		int AllKeys;
		string AllValues;

		switch (attr) {
		case 1:  // SELECT key
		case 2:  // SELECT value
		case 3:  // SELECT *
			useNonIndex = 1;
		  break;
		case 4: // SELECT COUNT(*)
		 fprintf(stdout, "%d\n", index.numberOfTuples());
			
		 break;
		}
	}
	  
	  else {
	  for(int i = 0; i < cond.size(); i ++){
		 int intForm = atoi(cond[i].value);
		if(intForm != 0){//value is a key
			if(cond[i].comp != SelCond::NE){//not "not equal"
				
				if(cond[i].comp == SelCond::EQ){//EQ
				
					index.locate(intForm, cursor);
					
					index.readForward(cursor, key2, rid2);
					
					if(attr == 2 || attr == 3){//get value if needed
						rf.read(rid2, key2, value2);
					}
					// print the tuple 
					if(key2 == intForm){
						count ++;
						switch (attr) {
						case 1:  // SELECT key
						
						  fprintf(stdout, "%d\n", key2);
						  break;
						case 2:  // SELECT value
						  fprintf(stdout, "%s\n", value2.c_str());
						  break;
						case 3:  // SELECT *
						   
						  fprintf(stdout, "%d '%s'\n", key2, value2.c_str());
						  break;
						}
					}					
				}
				if(cond[i].comp == SelCond::LT || cond[i].comp == SelCond::LE){//LE/LT
					int inc = 0;
					if(intForm > index.maxKey){
						index.locate(index.maxKey, cursor);
						inc = 1;
					}
					else
						index.locate(intForm, cursor);
						
					index.readForward(cursor, key2, rid2);
					if(attr == 2 || attr == 3){//get value if needed
						rf.read(rid2, key2, value2);
					}
					index.locate(0, cursor);//find left most node

					while(true){
					
						index.readForward(cursor, key3, rid3);
						if(attr == 2 || attr == 3){//get value if needed
							rf.read(rid3, key3, value3);
							
						}
					
						if(key3 == key2){//if at entry which is >= to intForm
							if(cond[i].comp == SelCond::LE || inc == 1){
								
								if(key3 == intForm || inc == 1){
									count ++;
									switch (attr) {
									case 1:  // SELECT key
									  fprintf(stdout, "%d\n", key3);
									  break;
									case 2:  // SELECT value
									  fprintf(stdout, "%s\n", value3.c_str());
									  break;
									case 3:  // SELECT *
						
									  fprintf(stdout, "%d '%s'\n", key3, value3.c_str());
									  break;
									}
								}
							}
							break;
						}
						
						count ++;
						switch (attr) {
						case 1:  // SELECT key
						  fprintf(stdout, "%d\n", key3);
						  break;
						case 2:  // SELECT value
						  fprintf(stdout, "%s\n", value3.c_str());
						  break;
						case 3:  // SELECT *
						
						fprintf(stdout, "%d '%s'\n", key3, value3.c_str());
						  break;
						}
					}
				
				}
				if(cond[i].comp == SelCond::GT || cond[i].comp == SelCond::GE){//GT GE
					
					index.locate(intForm, cursor); //find searchterm
					index.readForward(cursor, key2, rid2);
						
						if(attr == 2 || attr == 3){//get value if needed
							rf.read(rid2, key2, value2);
						}
					if((cond[i].comp == SelCond::GE && key2 >= intForm)||(cond[i].comp == SelCond::GT && key2 > intForm)){
						
						count ++; 
							switch (attr) {
							case 1:  // SELECT key
							  fprintf(stdout, "%d\n", key2);
							  break;
							case 2:  // SELECT value
							  fprintf(stdout, "%s\n", value2.c_str());
							  break;
							case 3:  // SELECT *
							
							fprintf(stdout, "%d '%s'\n", key2, value2.c_str());
							  break;
						}
					}
					while(true){//read forward until at the maxKey
						if(intForm > index.maxKey)
							break;
						index.readForward(cursor, key2, rid2);
						
						if(attr == 2 || attr == 3){//get value if needed
							rf.read(rid2, key2, value2);
						}
					
						count ++;
						switch (attr) {
						case 1:  // SELECT key
						  fprintf(stdout, "%d\n", key2);
						  break;
						case 2:  // SELECT value
						  fprintf(stdout, "%s\n", value2.c_str());
						  break;
						case 3:  // SELECT *
						
						fprintf(stdout, "%d '%s'\n", key2, value2.c_str());
						  break;
						}
						if(key2 >= index.maxKey){//if at maxKey (end of index leaf entries)
							break;
						}
					}
				}
			}
			else{//is "not equal"
				useNonIndex = 1;
			}
			
		}
		}
	}
  index.close();
  }
 if(useNonIndex ==1){
	  while (rid < rf.endRid()) {
		// read the tuple
		if ((rc = rf.read(rid, key, value)) < 0) {
		  fprintf(stderr, "Error: while reading a tuple from table %s\n", table.c_str());
		  goto exit_select;
		}

		// check the conditions on the tuple
		for (unsigned i = 0; i < cond.size(); i++) {
		  // compute the difference between the tuple value and the condition value
		  switch (cond[i].attr) {
		  case 1:
		diff = key - atoi(cond[i].value);
		break;
		  case 2:
		diff = strcmp(value.c_str(), cond[i].value);
		break;
		  }

		  // skip the tuple if any condition is not met
		  switch (cond[i].comp) {
		  case SelCond::EQ:
		if (diff != 0) goto next_tuple;
		break;
		  case SelCond::NE:
		if (diff == 0) goto next_tuple;
		break;
		  case SelCond::GT:
		if (diff <= 0) goto next_tuple;
		break;
		  case SelCond::LT:
		if (diff >= 0) goto next_tuple;
		break;
		  case SelCond::GE:
		if (diff < 0) goto next_tuple;
		break;
		  case SelCond::LE:
		if (diff > 0) goto next_tuple;
		break;
		  }
		}

		// the condition is met for the tuple. 
		// increase matching tuple counter
		count++;

		// print the tuple 
		switch (attr) {
		case 1:  // SELECT key
		  fprintf(stdout, "%d\n", key);
		  break;
		case 2:  // SELECT value
		  fprintf(stdout, "%s\n", value.c_str());
		  break;
		case 3:  // SELECT *
		  fprintf(stdout, "%d '%s'\n", key, value.c_str());
		  break;
		}

		// move to the next tuple
		next_tuple:
		++rid;
	  }
}
   
  // print matching tuple count if "select count(*)"
  if (attr == 4 && !cond.empty()) {
    fprintf(stdout, "%d\n", count);
  }
  rc = 0;

  // close the table file and return
  exit_select:
  rf.close();
  return rc;
}

RC SqlEngine::load(const string& table, const string& loadfile, bool index)
{
  /* your code here */
    
	const string recordName = table + ".tbl";
	RecordFile tempRecord;
	RecordId RI;
	RI.pid=0;
	RI.sid=0;
	tempRecord.open(recordName, 'w');
	ifstream filestr(loadfile.c_str(), ifstream::out| fstream::in);
  
	string str((std::istreambuf_iterator<char>(filestr)), std::istreambuf_iterator<char>());

	filestr.seekg (0, ios::end);
    int arraysize=filestr.tellg();
  	char beginFile[arraysize];
    filestr.seekg (0, ios::beg);
	
    int key;
    string value;
	
	string stringUse;
	const string* stringPtr = &stringUse;
if(index == true){
	const string tableIndex = table + ".idx";
	BTreeIndex index;
	index.open(tableIndex, 'w');

	while(filestr){	
		getline(filestr, stringUse);
		if(!filestr)
			break;
		parseLoadLine(*stringPtr, key, value);
		
		tempRecord.append(key, value, RI);	
		index.insert(key, RI);	
	}
	
	tempRecord.close();
	filestr.close();

  index.close();
  return 0;
}
else{
while(filestr){	
		getline(filestr, stringUse);
		if(!filestr)
			break;
		parseLoadLine(*stringPtr, key, value);
		
		tempRecord.append(key, value, RI);	
		
	}
	tempRecord.close();
	filestr.close();
}


  return 0;
}

RC SqlEngine::parseLoadLine(const string& line, int& key, string& value)
{
    const char *s;
    char        c;
    string::size_type loc;
    
    // ignore beginning white spaces
    c = *(s = line.c_str());
    while (c == ' ' || c == '\t') { c = *++s; }

    // get the integer key value
    key = atoi(s);

    // look for comma
    s = strchr(s, ',');
    if (s == NULL) { return RC_INVALID_FILE_FORMAT; }

    // ignore white spaces
    do { c = *++s; } while (c == ' ' || c == '\t');
    
    // if there is nothing left, set the value to empty string
    if (c == 0) { 
        value.erase();
        return 0;
    }

    // is the value field delimited by ' or "?
    if (c == '\'' || c == '"') {
        s++;
    } else {
        c = '\n';
    }

    // get the value string
    value.assign(s);
    loc = value.find(c, 0);
    if (loc != string::npos) { value.erase(loc); }

    return 0;
}
